package Core.plugin

import Core.{CPUSimple, Plugin}
import Core.ip.DCacheConfig
import Core._
import Core.ip._
import spinal.core._
import spinal.lib._
import spinal.lib.bus.amba4.axi.{Axi4ReadOnly, Axi4WriteOnly}

trait DCacheAccessService {
    def newDCacheAccess() : DCacheAccess
}

// ================ next level ports as master ==============
case class DCacheNextLevelCmd(p : DCacheConfig) extends Bundle{
    val addr = UInt(p.addressWidth bits)
    val len  = UInt(4 bits)
    val size = UInt(3 bits)
    val wen  = Bool()
    val wdata= Bits(p.busDataWidth bits)
    val wstrb= Bits(p.busDataWidth/8 bits)
}
case class DCacheNextLevelRsp(p : DCacheConfig) extends Bundle{
    val data = Bits(p.busDataWidth bits)
    val bresp= Bits(2 bits)
    val rvalid = Bool()
}
case class DCacheNextLevelPorts(p : DCacheConfig) extends Bundle with IMasterSlave{
    val cmd = Stream(DCacheNextLevelCmd(p))
    val rsp = Flow(DCacheNextLevelRsp(p))

    override def asMaster(): Unit = {
        master(cmd)
        slave(rsp)
    }
}

// ================ cpu and dcache ports ===============
case class DCacheAccessCmd(AW: Int, DW: Int) extends Bundle {
    val addr = UInt(AW bits)
    val wen  = Bool()
    val wdata= Bits(DW bits)
    val wstrb= Bits(DW/8 bits)
    val size = UInt(2 bits)
}
case class DCacheAccessRsp(DW: Int) extends Bundle {
    val data = Bits(DW bits)
}
case class DCacheAccess(AW: Int, DW: Int) extends Bundle with IMasterSlave{
    val cmd = Stream(DCacheAccessCmd(AW, DW))
    val rsp = Flow(DCacheAccessRsp(DW))
    val stall = Bool()
    override def asMaster(): Unit = {
        master(cmd)
        slave(rsp)
    }
    override def asSlave(): Unit = {
        slave(cmd)
        master(rsp)
    }
}

class DCachePlugin(val config : DCacheConfig) extends Plugin[CPUSimple] {
    import config._

    var dcache_access : DCacheAccess = null
    var dcacheReader : Axi4ReadOnly = null
    var dcacheWriter : Axi4WriteOnly = null

    override def setup(pipeline: CPUSimple): Unit = {
        import Core.LoongArch._
        import pipeline.config._
        dcache_access = pipeline.service(classOf[DCacheAccessService]).newDCacheAccess
    }

    override def build(pipeline: CPUSimple): Unit = {
        import pipeline._
        import pipeline.config._
        val dcache_config = DCachePlugin.this.config
        val dcacheMaster = master(DCacheAccess(dcache_config.addressWidth, dcache_config.cpuDataWidth)).setName("dcache")
        // connect dcache and cpu ports
        dcache_access.cmd <> dcacheMaster.cmd
        dcache_access.rsp <> dcacheMaster.rsp
        dcache_access.stall := False
    }
}